package runnerworm.ui;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import runnerworm.core.*;

/**
 * <p>Title: RunnerWormFrame</p>
 * <p>Description: This is here where all the game will be held.
 * The game interface is simple. A menu, where it'll be possible to
 * change the options; a place where we have the current score; and the
 * game it self, which will be held just above the score.</p>
 * @author Guilherme Mauro Germoglio Barbosa
 */

public class RunnerWormFrame
    extends JFrame
    implements ActionListener, MenuListener, KeyListener, EngineListener {

  /**
   * This will be the score that will be shown to the user.
   */
  private JLabel scoreLabel;
  /**
   * This will be where the game will be shown.
   */
  private JPanel gamePanel;
  /**
   * This will launch UserInterfaceEvents, which are needed to the
   * engine know what the UI is saying.
   */
  private UserInterfaceEventLauncher userInterfaceEventLauncher;
  /**
   * The button that represents the easy difficulty level.
   */
  private JRadioButtonMenuItem easyLevel;
  /**
   * The button that represents the easy difficulty level.
   */
  private JRadioButtonMenuItem mediumLevel;
  /**
   * The button that represents the easy difficulty level.
   */
  private JRadioButtonMenuItem hardLevel;
  /**
   * The game options.
   */
  private GameOptions gameOptions;

  /**
   * It will add all the menus this frame will have and the score label. Set
   * the window title, the size and add all the listeners.
   */
  public RunnerWormFrame() {
    // the title
    setTitle("RunnerWorm");
    // about the size
    setSize(300,300);
    setResizable(false);
    // about closing it
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    // adds the score
    addScoreLabel();
    // adds the JMenuBar and fills it
    addCompleteJMenuBar();
    // creates the UserInterfaceEventLauncher
    this.userInterfaceEventLauncher = new UserInterfaceEventLauncher();
    // creates the gamePanel
    gamePanel = new JPanel();
    gamePanel.setBackground(ColorTable.BACKGROUND_COLOR);
    getContentPane().add(gamePanel, BorderLayout.CENTER);
    // add keylistener
    addKeyListener(this);
  }


  /**
   * It will add the scoreLabel to the frame. The scoreLabel is where
   * the score will be shown.
   */
  private void addScoreLabel() {
    this.scoreLabel = new JLabel("0.0");
    JLabel staticScoreLabel = new JLabel("Score: ");
    JPanel scorePanel = new JPanel(new FlowLayout());
    scorePanel.setBorder(BorderFactory.createEtchedBorder());
    scorePanel.add(staticScoreLabel);
    scorePanel.add(scoreLabel);
    getContentPane().add(scorePanel, BorderLayout.NORTH);
  }

  /**
   * It will add the JMenuBar and fill it with all the menus needed.
   */
  private void addCompleteJMenuBar() {
    // create the JMenuBar
    JMenuBar menuBar = new JMenuBar();
    // add the JMenuBar
    setJMenuBar(menuBar);
    // add Game menu
    addGameMenu(menuBar);
    addHelpMenu(menuBar);
  }
  /**
   * It will create the Game menu, fills it with all its items and add it
   * to the menuBar specified.
   * @param menuBar The JMenuBar where the Game menu will be added.
   */
  private void addGameMenu(JMenuBar menuBar) {
    // create the game menu
    JMenu gameMenu = new JMenu("Game");
    gameMenu.setMnemonic('G');
    fillGameMenu(gameMenu);
    // add menu
    menuBar.add(gameMenu);
    // add the MenuListener
    gameMenu.addMenuListener(this);
  }

  /**
   * Fills the JMenu specified with the items: New Game, Options, High
   * Scores and Quit.
   * @param jMenu The JMenu specified.
   */
  private void fillGameMenu(JMenu jMenu) {
    // adds New Game
    JMenuItem newGameItem = new JMenuItem("New Game", 'N');
    newGameItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2,0));
    newGameItem.addActionListener(this);
    jMenu.add(newGameItem);
    // adds Options
    JMenu optionsJMenu = new JMenu("Options");
    optionsJMenu.setMnemonic('O');
    optionsJMenu.addActionListener(this);
    optionsJMenu.addMenuListener(this);
    fillOptionsJMenu(optionsJMenu);
    jMenu.add(optionsJMenu);
    // adds HighScores
    JMenuItem highScoresItem = new JMenuItem("High Scores", 'H');
    highScoresItem.addActionListener(this);
    jMenu.add(highScoresItem);
    // adds a separator
    jMenu.addSeparator();
    // adds Quit
    JMenuItem quitItem = new JMenuItem("Quit", 'Q');
    quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,InputEvent.CTRL_MASK));
    quitItem.addActionListener(this);
    jMenu.add(quitItem);
  }
  /**
   * Will add to this JMenu its options, like the difficulty level.
   * @param optionsJMenu The optionsJMenu.
   */
  private void fillOptionsJMenu(JMenu optionsJMenu) {
    JMenu difficultyJMenu = new JMenu("Difficulty");
    ButtonGroup levelButtonGroup = new ButtonGroup();

    easyLevel = new JRadioButtonMenuItem("Easy");
    easyLevel.addActionListener(this);
    levelButtonGroup.add(easyLevel);
    difficultyJMenu.add(easyLevel);


    mediumLevel = new JRadioButtonMenuItem("Medium");
    mediumLevel.addActionListener(this);
    levelButtonGroup.add(mediumLevel);
    difficultyJMenu.add(mediumLevel);

    hardLevel = new JRadioButtonMenuItem("Hard");
    hardLevel.addActionListener(this);
    levelButtonGroup.add(hardLevel);
    difficultyJMenu.add(hardLevel);

    optionsJMenu.add(difficultyJMenu);
  }

  /**
   * It will create the Help menu, fills it with all its items and add it
   * to the menuBar specified.
   * @param menuBar The JMenuBar where the Help menu will be added.
   */
  private void addHelpMenu(JMenuBar menuBar) {
    // create the game menu
    JMenu helpMenu = new JMenu("Help");
    helpMenu.setMnemonic('H');
    fillHelpMenu(helpMenu);
    // add menu
    menuBar.add(helpMenu);
    // add the MenuListener
    helpMenu.addMenuListener(this);
  }

  /**
   * Fills the JMenu specified with the items: Help and About...
   * @param jMenu The JMenu specified.
   */
  private void fillHelpMenu(JMenu jMenu) {
    // adds Help
    JMenuItem helpItem = new JMenuItem("Help", 'H');
    helpItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1,0));
    helpItem.addActionListener(this);
    jMenu.add(helpItem);
    // add a separator
    jMenu.addSeparator();
    // adds About
    JMenuItem aboutItem = new JMenuItem("About", 'A');
    aboutItem.addActionListener(this);
    jMenu.add(aboutItem);
  }


  /**
   * It takes care of ActionEvents.
   * @param evt The ActionEvent. Normally related to the menus.
   */
  public void actionPerformed(ActionEvent evt) {
    if (evt.getSource()instanceof JMenuItem) {
      String arg = evt.getActionCommand();
      if (arg.equals("New Game")) {
        gamePanel.repaint();
        userInterfaceEventLauncher.createNewGameEvent(gameOptions == null ?
            new GameOptions() : gameOptions, scoreLabel,
            gamePanel);
      }
      else if (arg.equals("High Scores"))
        userInterfaceEventLauncher.createHighScoresEvent();
      else if (arg.equals("Quit"))
        userInterfaceEventLauncher.createQuitGame();
      else if (arg.equals("Help"))
        userInterfaceEventLauncher.createHelpEvent();
      else if (arg.equals("About"))
        userInterfaceEventLauncher.createAboutEvent();
      else if (arg.equals("Easy"))
        gameOptions.setCurrentLevel(GameOptions.EASY_LEVEL);
      else if (arg.equals("Medium"))
        gameOptions.setCurrentLevel(GameOptions.MEDIUM_LEVEL);
      else if (arg.equals("Hard"))
        gameOptions.setCurrentLevel(GameOptions.HARD_LEVEL);
    }
  }

  /**
   * Adds a listener to this class' events.
   * @param l The UserInterfaceEvents listener.
   */
  public void addUserInterfaceListener(UserInterfaceListener l) {
    userInterfaceEventLauncher.addUserInterfaceListener(l);
  }

  /**
   * Returns the scoreLabel - where the score will be shown to the user.
   * @return JLabel Where the score will be shown.
   */
  public JLabel getScoreLabel() {
    return scoreLabel;
  }

  /**
   * Does what it has to be done when a menu is canceled.
   * @param evt The MenuEvent related to the cancel of the menu
   */
  public void menuCanceled(MenuEvent evt) {

  }

  /**
   * Does what it has to be done when a menu is deselected.
   * @param evt The MenuEvent related to the cancel of the menu
   */
  public void menuDeselected(MenuEvent evt) {

  }

  /**
   * Does what it has to be done when a menu is selected.
   * @param evt The MenuEvent related to the cancel of the menu
   */
  public void menuSelected(MenuEvent evt) {
    if (evt.getSource() instanceof JMenu) {
      String arg = ((JMenu)evt.getSource()).getText();
      if (arg.equals("Options"))
        userInterfaceEventLauncher.createOptionsEvent();
    }
  }

  /**
   * Does what it has to be done when a key is typed.
   * @param evt The KeyEvent captured when a key is typed.
   */
  public void keyTyped(KeyEvent evt) {


  }

  /**
   * Does what it has to be done when a key is pressed.
   * @param evt The KeyEvent captured when a key is pressed.
   */
  public void keyPressed(KeyEvent evt) {
    switch (evt.getKeyCode()) {
      case (KeyEvent.VK_SPACE):
        userInterfaceEventLauncher.createWormGoingUpEvent();
        break;
    }
  }

  /**
   * Does what it has to be done when a key is released.
   * @param evt The KeyEvent captured when a key is released.
   */
  public void keyReleased(KeyEvent evt) {
    switch (evt.getKeyCode()) {
      case (KeyEvent.VK_SPACE):
        userInterfaceEventLauncher.createWormGoingDownEvent();
        break;
    }
  }

  /**
   * Provides what to do when the engine sends a GameOptions object.
   * @param gameOptions the GameOptions.
   */
  public void gotOptionsAvailable(GameOptions gameOptions) {
    if (this.gameOptions == null) {
      this.gameOptions = gameOptions;
      switch (gameOptions.getCurrentLevel()) {
        case GameOptions.EASY_LEVEL:
          easyLevel.doClick();
          break;
        case GameOptions.MEDIUM_LEVEL:
          mediumLevel.doClick();
          break;
        case GameOptions.HARD_LEVEL:
          hardLevel.doClick();
          break;
      }
    }

  }

  /**
   * Provides what to do when the HighScoresAvailableEvent is captured.
   * @param highScoresString the game's high scores.
   */
  public void gotHighScoresAvailable(String highScoresString) {
    JOptionPane.showMessageDialog(this, highScoresString, "High Scores", JOptionPane.DEFAULT_OPTION);
  }

  /**
   * Provides what to do when the AboutAvailableEvent is captured.
   * @param aboutText the game's "about".
   */
  public void gotAboutAvailable(String aboutText) {
    JOptionPane.showMessageDialog(this, aboutText, "About", JOptionPane.DEFAULT_OPTION);
  }

  /**
   * Provides what to do when the HelpAvailableEvent is captured.
   * @param helpText the game's "help".
   */
  public void gotHelpAvailable(String helpText) {
    JOptionPane.showMessageDialog(this, helpText, "Need help?", JOptionPane.DEFAULT_OPTION);
  }

  /**
   * Provides what to do when the ExceptionAvailableEvent is captured.
   * @param e The exception caught.
   */
  public void gotExceptionAvailable(Exception e) {
    JOptionPane.showMessageDialog(this, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
  }

  /**
   * Will ask the player name and return it to the engine.
   */
  public void askPlayerName() {
    String playerName = JOptionPane.showInputDialog(this, "Give us your name:", "High score!", JOptionPane.DEFAULT_OPTION);
    if (playerName == null || playerName.equals(""))
      playerName = "Anonymous";
    userInterfaceEventLauncher.createPlayerNameEvent(playerName);
  }

}